#!/bin/bash
# 李广龙
# 脚本说明:

INSTRUCTIONS="脚本说明"
# author: 李广龙
# email: nuo010@126.com
version=v1.5


ALPINE_VERSION=alpine:3.12


#======================================
# 默认实例
# 多实例要在项目中使用随机端口启动
INSTANCES=1
# 手动启动还是自动启动 (默认手动启动 false) docker 方式
################################################
################################################
#################devOps#########################
################################################
################################################
# 或者执行脚本时添加 devops 参数即可
AUTOMATIC=false
#AUTOMATIC=true

# 自动部署方式
# java、docker
DEVOPSMODE=docker
#################################################################

#################################################################
# 项目名字 默认空会获取同级目录的jar包名
# 项目所在路径
# 项目绝对路径
SERVICE_NAME=""
SERVICE_PATH=""
SERVER_ALL_PATH=""
# 默认日志文件夹
LOG_PATH=/logs
# SpringBoot外部配置文件夹
CONFIG_PATH=/config
# 服务文件备份文件夹
BACK_PATH=/back


IP=$(ifconfig -a | grep inet | grep -v 127.0.0.1 | grep -v inet6 | awk '{print $2}' | tr -d "addrs" | tr '\n' ';')
# 当前时间
DATEVERSION=$(date +'%Y%m%d%H%M')
# 获取当前sh绝对路径
script_dir=$(readlink -f "$0")
# 获取父级路径
bootpath=$(dirname "$script_dir")
logspath=$bootpath$LOG_PATH
configpath=$bootpath$CONFIG_PATH
backpath=$bootpath$BACK_PATH





if [ ${#SERVICE_PATH} == 0 ]; then
  SERVICE_PATH=$(ls "$bootpath" -S | awk '{print $0}' | head -1)
fi
if [ ${#SERVICE_NAME} == 0 ]; then
  SERVICE_NAME=${SERVICE_PATH}
fi
SERVER_ALL_PATH="${bootpath}/${SERVICE_PATH}"

# 颜色定义
RED='\e[1;31m'
GREEN='\e[1;32m'
YELLOW='\033[1;33m'
BLUE='\E[1;34m'
PINK='\E[1;35m'
RES='\033[0m'


createDockerfile() {
chmod +x "$SERVICE_NAME"
cat > ./Dockerfile << EOF
FROM $ALPINE_VERSION
MAINTAINER LiGuangLong
ADD ${SERVICE_PATH} /code/${SERVICE_PATH}
WORKDIR /code
#VOLUME $LOG_PATH
#EXPOSE $OPEN_PORT
ENV TZ=Asia/Shanghai
ENTRYPOINT ["./$SERVICE_NAME"]
EOF
}

createDockerIgnore() {
cat > ./.dockerignore << EOF
log?
back
EOF
}
createConfLogs() {
  mkdir config
  mkdir logs
  mkdir back
}

# 删除docker同服务的镜像
deleteOldAllImage() {
#  echo -e "${RED}要删除镜像手动 请手动删除 docker备份基于镜像备份${RES}"
  echo -e "${RED}清除当前docker镜像${RES}"
  arr=$(docker images | grep "${SERVICE_NAME}" | awk '{print $2}')
  #echo ================
  #echo docker image rmi -f "$SERVICE_NAME":"$arr"
  docker image rmi -f "$SERVICE_NAME":"$arr" >> /dev/null 2>&1;
  #docker image ls
}
deleteServerNameAllImage() {
  echo -e "${RED}删除${SERVICE_NAME}镜像,只保留最新的5个${RES}"
  echo -e "${RED}版本不同镜像id相同的需要手动进行删除${RES}"
  docker rmi -f $(docker images --no-trunc | grep ${SERVICE_NAME} | tail -n +5 |tr -s ' ' | cut -d ' ' -f 3 | cut -d ':' -f 2)
}
# delete old containers
deleteOldContainer() {
  OLD_INSTANCES=$(docker container ps -a | grep -i $SERVICE_NAME | wc -l);
  for((i=0;i<$OLD_INSTANCES;i++));
  do
    docker container stop $SERVICE_NAME-$i >> /dev/null 2>&1;
    docker container rm -f $SERVICE_NAME-$i >> /dev/null 2>&1;
  done
  # rm -rf $bootpath/logs;
  if docker container ps -a | grep -i $SERVICE_NAME;then
    echo -e $RED hase $OLD_INSTANCES instances. $RES
  fi
#  docker container ps
}

# 打包docker镜像
# 已当前运行脚本的时间为版本号
buildImage() {
  docker build -t $SERVICE_NAME:$DATEVERSION . ;
#  docker image ls
}

# 运行镜像
# 默认单实例 运行1个
runImage() {
  for((i=0;i < $INSTANCES;i++));
  do
    name=$SERVICE_NAME-$i
    port=$(($HOST_PORT+$i))
    # 强制删除已存在的容器
    docker container rm -f $name >> /dev/null 2>&1
#    echo "创建容器是:$name:$IP:$port:$OPEN_PORT"
    echo -e $GREEN "创建容器是:>>>>>>>>>>>>>>>>>>>>>>: $name" $RES
    # 只挂载logs日志文件配置文件不挂载
    docker run \
    --name "$name" \
    --network host \
    --restart=always \
    -d "$SERVICE_NAME":"$DATEVERSION"
    # --privileged=true \ 默认是普通用户 使用root权限
    CONTAINERID_NEW=$(docker container ps -a | grep "${name}"| awk '{print $NF}')
    echo -e 已创建容器: $PINK"$CONTAINERID_NEW"$RES
    if [ $i -lt $INSTANCES ];then
      sleep 1;
    fi
  done
#  docker container ps
}
startContainer() {
  for((i=0;i < $INSTANCES;i++));
  do
    name=$SERVICE_NAME-$i
    port=$(($HOST_PORT+$i))

    docker container start $name >> /dev/null 2>&1
#    echo start container is $name:$port:$OPEN_PORT
    echo 启动容器: $name
    if [ $i -lt $INSTANCES ];then
      sleep 1;
    fi
  done
  docker container ps
}
restartContainer() {
  for((i=0;i < $INSTANCES;i++));
  do
    name=$SERVICE_NAME-$i
    port=$(($HOST_PORT+$i))

    docker container restart $name >> /dev/null 2>&1
#    echo restart container is $name:$port:$OPEN_PORT
    echo 重启容器 $name
    if [ $i -lt $INSTANCES ];then
      sleep 1;
    fi
  done
  docker container ps
}
stopContainer() {
  for((i=0;i < $INSTANCES;i++));
  do
    name=$SERVICE_NAME-$i
    port=$(($HOST_PORT+$i))
    docker container stop $name >> /dev/null 2>&1
#    echo stop container is $name:$port:$OPEN_PORT
    echo 停止容器: $name
    if [ $i -lt $INSTANCES ];then
      sleep 1;
    fi
  done
  docker container ps
}
rmContainer() {
  for((i=0;i < $INSTANCES;i++));
  do
    name=$SERVICE_NAME-$i
    port=$(($HOST_PORT+$i))
    docker container rm $name >> /dev/null 2>&1
#    echo rm container is $name:$port:$OPEN_PORT
    echo 删除容器: $name
    if [ $i -lt $INSTANCES ];then
      sleep 1;
    fi
  done
  docker container ps
}
viewContainerLog() {
  if [ $INSTANCES -eq 1 ];then
    showLog $SERVICE_NAME-0
  else
    # 存在多个容器时进行选择查看
    echo -e $GREEN show logs for containers: $RES
    docker ps -a | grep ${SERVICE_NAME}| awk '{print $1, $2, $(NF-1), $NF}'
    read -p '请输入容器id或name:' input
    showLog $input
  fi
}
showJarAllLog() {
  echo "${bootpath}"/logs/${LOG_FILENAME}
  tail -n 100 "${bootpath}"/logs/${LOG_FILENAME}
}
showLog() {
  docker container logs --tail=300 "$1"
}

current() {
  echo 
  echo -e "${PINK}当前时间:$(date +'%Y-%m-%d %T')${RES}"
  echo 
}
dockerRestart() {
  echo " 重启$SERVICE_NAME容器"
  docker restart "$SERVICE_NAME"-0
  echo -e "$RED 重启$SERVICE_NAME-0容器 成功!!!$RES"
}

dockerLogs() {
    if [ $INSTANCES -eq 1 ];then
      dockerLogsF $SERVICE_NAME-0
    else
      # 存在多个容器时进行选择查看
      echo -e $GREEN show logs for containers: $RES
      docker ps -a | grep ${SERVICE_NAME}| awk '{print $1, $2, $(NF-1), $NF}'
      read -p '请输入容器id或name:' input
      dockerLogsF $input
    fi
}
dockerLogsF() {
  echo "查看$1容器日志"
  docker logs "$1" -f
}
sendMail() {
  # 有的服务器可能因为库的原因发送不了邮件
  echo 发送邮件通知
  echo '{"data":"版本:'$DATEVERSION'IP:'$IP'","dizi":"nuo010@126.com","title":"服务部署通知:'$SERVICE_NAME'"}'
  curl 'https://elel.fun/fastjson/sendMail' -H "Content-Type:application/json" -H 'Authorization:bearer' -X POST -d '{"data":"版本:'$DATEVERSION'----IP:'$IP'","dizi":"nuo010@126.com","title":"服务部署通知:'$SERVICE_NAME'"}'
}


var() {
  echo IP "$IP"
  echo 服务名称 "$SERVICE_NAME"
  echo 服务文件 "${bootpath}/${SERVICE_PATH}"
  echo 实例数量 $INSTANCES
#  echo docker容器端口 $OPEN_PORT
#  echo docker主机端口 $HOST_PORT
  echo -e docker网络映射规则 $GREEN --network host $RES
  echo docker基础镜像版本 $ALPINE_VERSION
  echo docker镜像版本 "$DATEVERSION"
  echo deploy版本 "$version"
  echo configpath "$configpath"
  echo logspath "$logspath"
#  echo 默认查看log文件 "$LOG_FILENAME"
  echo -e "当前脚本说明:" $PINK $INSTRUCTIONS $RES
  echo
  echo
  echo
}

# setting env var
setEnvironmentVariable() {
  ARRT=$1
  ARRT_NAME=$(echo "${ARRT}" | awk -F '=' '{print $1}')
  ARRT_VALUE=$(echo "${ARRT}" | awk -F '=' '{print $2}')
  # echo $ARRT_NAME is $ARRT_VALUE
  # shellcheck disable=SC2086
  if [ $ARRT_NAME == 'name' ]; then
    SERVICE_NAME=$ARRT_VALUE
  elif [ "$ARRT_NAME" == 'port' ]; then
    OPEN_PORT=$ARRT_VALUE
  elif [ "$ARRT_NAME" == 'ip' ]; then
    IP=$ARRT_VALUE
  elif [ $ARRT_NAME == 'i' ]; then
    INSTANCES=$ARRT_VALUE
  else
    echo 
    echo -e $RED $ARRT no matches found. $RES
    echo 
  fi
}
volumeList() {
  docker volume ls -qf dangling=true
}

deleteVolumeList() {
  docker volume rm $(docker volume ls -qf dangling=true)
}
isPort() {
  echo "************ 检查端口占用(${HOST_PORT}) **************"
  echo "************ 只能kill非docker容器占用的端口*************"
  port=$(netstat -nlp |grep :"${HOST_PORT}" | awk '{print $7}')
  port=${port%%/*}
  if [ ${#port} -gt 1  ]; then
    echo "端口占用-进程id: $port"
    kill -9 "$port"
    echo "开始 kill ${HOST_PORT} 端口占用进程!"
    if [ "$?" -eq 0 ]; then
            echo -e "\033[31mkill $port 成功!\033[0m"
    else
            echo -e "\033[31mkill $port 失败\033[0m"
    fi
  fi
}
isPid() {
  echo "************** 查找进程($SERVICE_PATH) ****************"
  pid=$(ps -ef | grep "$SERVICE_PATH" | grep -v grep | awk '{print $2}')
  if [ -n "$pid" ]; then
    echo "${bootpath}/${SERVICE_PATH}进程id: $pid"
    kill -9 "$pid"
    if [ "$?" -eq 0 ]; then
            echo -e "\033[31mkill $pid 成功!\033[0m"
    else
            echo -e "\033[31mkill $pid 失败\033[0m"
    fi
  fi
}
# 部署方式为后台运行jar时使用此方法
backjar() {
  cp "${SERVICE_NAME}.jar" back/"${DATEVERSION}".jar
  echo -e "$GREEN 备份路径:back/${DATEVERSION}.jar $RES"
  echo "备份完成!"
}
rmjar() {
  #保留文件数
  ReservedNum=5
  #显示文件数， *.*可以改为指定文件类型
  cd $backpath
  FileNum=$(ls -l *.jar | grep ^- | wc -l)
  while(( $FileNum > $ReservedNum ))
  do
    #取最旧的文件，*.*可以改为指定文件类型
    OldFile=$(ls | head -1)
    echo -e "$RED 清理过时备份文件:$backpath/$OldFile $RES"
    rm -f $backpath'/'$OldFile
    let "FileNum--"
  done
  cd $bootpath
}

functionItems() {
  echo 
  echo -e "$GREEN = 0. 部署单个docker容器 $RES"
  echo -e "$BLUE = 1. 在当前目录创建 Dockerfile,dockerIgnore文件config,logs,back文件夹  $RES"
  echo -e "$BLUE = 2. 打包镜像 $SERVICE_NAME $RES"
  #echo -e "$BLUE = 3. 运行镜像 $SERVICE_NAME $RES"
  #echo -e "$BLUE = 4. 开启 $SERVICE_NAME 容器 $RES"
  #echo -e "$BLUE = 5. 重启 $SERVICE_NAME 容器 $RES"
  #echo -e "$BLUE = 6. 停止 $SERVICE_NAME 容器 $RES"
  #echo -e "$BLUE = 7. 删除 $SERVICE_NAME 容器  $RES"
  #echo -e "$BLUE = 8. 删除 镜像 $SERVICE_NAME $RES"
  echo -e "$BLUE = 3. 查看 $SERVICE_NAME ($INSTANCES)容器 日志 (-n 300)$RES"
  echo -e "$GREEN = 4. 后台部署jar ${bootpath}/${SERVICE_PATH} $RES"
  echo -e "$BLUE = 5. 删除${bootpath}/${SERVICE_PATH} docekr备份镜像 $RES"
  echo -e "$BLUE = 6. 备份${SERVER_ALL_PATH}到当前back文件夹下 $RES"
  echo -e "$BLUE = 7. 重启 $SERVICE_NAME ($INSTANCES)容器(重新加载config下配置文件) $RES"
  echo -e "$YELLOW = 8. 查看 $SERVICE_NAME ($INSTANCES)容器 日志(-f) $RES"
  echo -e "$RED = 9. 全部停止容器 $SERVICE_NAME ($INSTANCES)容器 $RES"
  echo -e "$BLUE = 10. 全部启动容器 $SERVICE_NAME ($INSTANCES)容器 $RES"
  echo -e "$BLUE = 11. 全部删除容器 $SERVICE_NAME ($INSTANCES)容器 $RES"
  echo -e "$RED = 12. 结束后台jar包 ${bootpath}/${SERVICE_PATH}  $RES"
  echo -e "$BLUE = 13. 删除全部已经停止的容器  $RES"
  echo -e "$BLUE = 14. 停止并删除全部容器  $RES"
  echo -e "$BLUE = 15. 删除所有未使用的镜像  $RES"
  echo -e "$BLUE = 16. 清理 none 镜像  $RES"

  echo
}
upgrade() {
    backjar
    rmjar
    deleteServerNameAllImage
    echo -e "${GREEN}删除容器成功${RES}"
    #deleteOldAllImage
    echo -e "${GREEN}备份镜像成功${RES}"
    buildImage
    echo -e "${GREEN}打包镜像成功${SERVICE_NAME}${RES}"
    #isPort 不需要检查端口
    runImage
    echo -e "${GREEN}运行镜像成功${RES}"
}
runjars() {
  isPid
  #isPort #访问和项目端口可能不一样不检测端口
  # 运行前先备份
  rmjar
  backjar
  runjar
  ps -ef | grep "$SERVICE_PATH"
  #ps -ef | grep "java -jar"
  echo -e "${GREEN}后台启动jar成功！${RES}"
}

# shellcheck disable=SC2120
main() {
  functionItems
  read -p '输入功能编号: (任意键退出)' input
  echo "输入编号:$input"
  case $input in
    0)
    upgrade
    ;;
    1)
    createDockerfile
    createDockerIgnore
    echo -e "${GREEN}Dockerfile 创建成功,默认基于 $ALPINE_VERSION $RES"
    cat Dockerfile
    echo -e "${GREEN}dockerIgnore 创建成功$RES"
    cat .dockerignore
    createConfLogs
    echo -e "${GREEN}创建config、logs、ack 文件夹成功$RES"
    ;;
    2)
    buildImage
    echo -e "${GREEN}创建镜像成功,镜像名字:$SERVICE_NAME 版本:$DATEVERSION $RES"
    ;;
    3)
    #isPort
    #runImage
    #echo -e "${GREEN}运行镜像${RES}"
    #============
    viewContainerLog
    echo -e "${GREEN}查看容器日志${RES}"
    ;;
    4)
    #isPort
    #startContainer
    #echo -e "${GREEN}开启容器${RES}"
    #==========
    runjars
    ;;
    5)
    #restartContainer
    #echo -e "${GREEN}重启容器${RES}"
    # 如果进行删除不掉 是因为进行id一样，使用标签的方式进行删除即可
    deleteServerNameAllImage
    ;;
    6)
    #stopContainer
    #echo -e "${GREEN}停止容器${RES}"
    backjar
    ;;
    7)
    # 重启  重新加载配置文件
    #dockerRestart
    restartContainer
    ;;
    8)
    # 查看输出日志 -f 连续
    dockerLogs
    ;;
    9)
    #deleteOldContainer
    #echo -e "${GREEN}删除容器${RES}"
    stopContainer
    echo -e "${GREEN}全部停止容器:${SERVICE_NAME}${RES}"
    ;;
    10)
    #deleteOldContainer
    #echo -e "${GREEN}删除容器${RES}"
    startContainer
    echo -e "${GREEN}全部启动容器:${SERVICE_NAME}${RES}"
    ;;
    11)
    #deleteOldContainer
    #echo -e "${GREEN}删除容器${RES}"
    rmContainer
    echo -e "${GREEN}全部删除容器:${SERVICE_NAME}${RES}"
    ;;
    12)
    # 结束java后台启动的jar包
    isPid
    ps -ef | grep "$SERVICE_PATH"
    echo -e "${GREEN}结束后台运行的jar包:${SERVICE_NAME}${RES}"
    ;;
    13)
    # 删除全部已经停止的容器
    docker rm $(docker ps -aq)
    ;;
    14)
    # 停止并删除全部容器
    docker stop $(docker ps -q) & docker rm $(docker ps -aq)
    ;;
    15)
    # 删除所有未使用的镜像
    # 删除镜像之前你要保证镜像没有被使用，否则删除不了，所以需要删除全部容器的时候，你需要先执行上边的命令，然后再执行下边这条命令
    docker rmi -f $(docker images -qa)
    ;;
    16)
    # 清理none镜像
    docker rmi -f $(docker images | grep "<none>" | awk "{print $3}")
    ;;
    *)
    echo -e "${RED}退出sh脚本${RES}"
    exit 0
    ;;
  esac
}
echo -e "${YELLOW}当前工作目录:${bootpath}${RES}"
#查看目录文件
cd "$bootpath" || exit;ls -all;
#for arg in $@
#do
#  setEnvironmentVariable $arg
#done
#版本信息
#readme
#当前时间
current
#变量值
var



# 判断是自动部署还是手动部署
if [ $AUTOMATIC = true ] || [ "$1" == "devops" ]; then
    # 判断是jar还是docker
    if [ "docker" == $DEVOPSMODE ]; then
        upgrade
      else
        runjars
    fi
    if [ $SENDMAIL = true ]; then
        sendMail
    fi
    else
    while true
    do
      main
      sleep 1
    done
fi

